<!DOCTYPE HTML>
<html lang="en" class="sidebar-visible no-js">

<head>
    <!-- Book generated using mdBook -->
    <meta charset="UTF-8">
    <title>音阶生成器 - exercisms.io 快速练习</title>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
    <meta name="description" content="exercisms 的练习，网页版.">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="theme-color" content="#ffffff" />

    <link rel="shortcut icon" href="../favicon.png">
    <link rel="stylesheet" href="../css/variables.css">
    <link rel="stylesheet" href="../css/general.css">
    <link rel="stylesheet" href="../css/chrome.css">
    <link rel="stylesheet" href="../css/print.css" media="print">

    <!-- Fonts -->
    <link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
    <link href="https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800"
        rel="stylesheet" type="text/css">
    <link href="https://fonts.googleapis.com/css?family=Source+Code+Pro:500" rel="stylesheet" type="text/css">

    <!-- Highlight.js Stylesheets -->
    <link rel="stylesheet" href="../highlight.css">
    <link rel="stylesheet" href="../tomorrow-night.css">
    <link rel="stylesheet" href="../ayu-highlight.css">

    <!-- Custom theme stylesheets -->
    
    <link rel="stylesheet" href="../theme/custom.css">
    

    
</head>

<body class="light">
    <!-- Provide site root to javascript -->
    <script type="text/javascript">var path_to_root = "../";</script>

    <!-- Work around some values being stored in localStorage wrapped in quotes -->
    <script type="text/javascript">
        try {
            var theme = localStorage.getItem('mdbook-theme');
            var sidebar = localStorage.getItem('mdbook-sidebar');

            if (theme.startsWith('"') && theme.endsWith('"')) {
                localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
            }

            if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
                localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
            }
        } catch (e) { }
    </script>

    <!-- Set the theme before any content is loaded, prevents flash -->
    <script type="text/javascript">
        var theme;
        try { theme = localStorage.getItem('mdbook-theme'); } catch (e) { }
        if (theme === null || theme === undefined) { theme = 'light'; }
        document.body.className = theme;
        document.querySelector('html').className = theme + ' js';
    </script>

    <!-- Hide / unhide sidebar before it is displayed -->
    <script type="text/javascript">
        var html = document.querySelector('html');
        var sidebar = 'hidden';
        if (document.body.clientWidth >= 1080) {
            try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch (e) { }
            sidebar = sidebar || 'visible';
        }
        html.classList.remove('sidebar-visible');
        html.classList.add("sidebar-" + sidebar);
    </script>

    <nav id="sidebar" class="sidebar" aria-label="Table of contents">
        <ol class="chapter"><li><a href="../index.html"><strong aria-hidden="true">1.</strong> 超过 88 道练习，任点</a></li><li><a href="../low.html"><strong aria-hidden="true">2.</strong> 易</a></li><li><ol class="section"><li><a href="../hello-world/README.zh.html"><strong aria-hidden="true">2.1.</strong> hello world</a></li><li><a href="../gigasecond/README.zh.html"><strong aria-hidden="true">2.2.</strong> 千兆秒 &gt;&lt; Gigasecond)</a></li><li><a href="../leap/README.zh.html"><strong aria-hidden="true">2.3.</strong> 闰年 &gt;&lt; Leap</a></li><li><a href="../raindrops/README.zh.html"><strong aria-hidden="true">2.4.</strong> 雨滴声 &gt;&lt; Raindrops</a></li><li><a href="../reverse-string/README.zh.html"><strong aria-hidden="true">2.5.</strong> 反转字符串 &gt;&lt; Reverse String</a></li><li><a href="../nth-prime/README.zh.html"><strong aria-hidden="true">2.6.</strong> 第 n 个素数 &gt;&lt; Nth Prime</a></li><li><a href="../bob/README.zh.html"><strong aria-hidden="true">2.7.</strong> 迟钝孩子 &gt;&lt; Bob</a></li><li><a href="../beer-song/README.zh.html"><strong aria-hidden="true">2.8.</strong> 啤酒之歌 &gt;&lt; Beer Song</a></li><li><a href="../proverb/README.zh.html"><strong aria-hidden="true">2.9.</strong> 谚语串烧 &gt;&lt; Proverb</a></li><li><a href="../difference-of-squares/README.zh.html"><strong aria-hidden="true">2.10.</strong> 平方差 &gt;&lt; Difference Of Squares</a></li><li><a href="../sum-of-multiples/README.zh.html"><strong aria-hidden="true">2.11.</strong> 倍数之和 &gt;&lt; Sum Of Multiples</a></li><li><a href="../grains/README.zh.html"><strong aria-hidden="true">2.12.</strong> 谷物 &gt;&lt; Grains</a></li><li><a href="../pythagorean-triplet/README.zh.html"><strong aria-hidden="true">2.13.</strong> 勾股数 &gt;&lt; Pythagorean Triplet</a></li><li><a href="../prime-factors/README.zh.html"><strong aria-hidden="true">2.14.</strong> 素数因子 &gt;&lt; Prime Factors</a></li><li><a href="../series/README.zh.html"><strong aria-hidden="true">2.15.</strong> 子串 &gt;&lt; Series</a></li><li><a href="../armstrong-numbers/README.zh.html"><strong aria-hidden="true">2.16.</strong> 水仙花数 &gt;&lt; Armstrong Numbers</a></li><li><a href="../collatz-conjecture/README.zh.html"><strong aria-hidden="true">2.17.</strong> 3n+1 猜想 &gt;&lt; Collatz Conjecture</a></li><li><a href="../diffie-hellman/README.zh.html"><strong aria-hidden="true">2.18.</strong> 迪菲-赫尔曼密钥交换 &gt;&lt; Diffie Hellman</a></li></ol></li><li><a href="../medium.html"><strong aria-hidden="true">3.</strong> 中等</a></li><li><ol class="section"><li><a href="../saddle-points/README.zh.html"><strong aria-hidden="true">3.1.</strong> 鞍点 &gt;&lt; Saddle Points</a></li><li><a href="../isogram/README.zh.html"><strong aria-hidden="true">3.2.</strong> 等值线 &gt;&lt; Isogram</a></li><li><a href="../say/README.zh.html"><strong aria-hidden="true">3.3.</strong> 英文说数字 &gt;&lt; Say</a></li><li><a href="../run-length-encoding/README.zh.html"><strong aria-hidden="true">3.4.</strong> 游程编码 &gt;&lt; Run Length Encoding</a></li><li><a href="../isbn-verifier/README.zh.html"><strong aria-hidden="true">3.5.</strong> 图书编号 &gt;&lt; ISBN Verifier</a></li><li><a href="../perfect-numbers/README.zh.html"><strong aria-hidden="true">3.6.</strong> 数字也能分类 &gt;&lt; Perfect Numbers</a></li><li><a href="../clock/README.zh.html"><strong aria-hidden="true">3.7.</strong> 时钟 &gt;&lt; Clock</a></li><li><a href="../dot-dsl/README.zh.html"><strong aria-hidden="true">3.8.</strong> DOT DSL</a></li><li><a href="../hamming/README.zh.html"><strong aria-hidden="true">3.9.</strong> 汉明距离 &gt;&lt; Hamming</a></li><li><a href="../simple-linked-list/README.zh.html"><strong aria-hidden="true">3.10.</strong> 简单链表 &gt;&lt; Simple Linked List</a></li><li><a href="../pascals-triangle/README.zh.html"><strong aria-hidden="true">3.11.</strong> 杨辉三角形 &gt;&lt; Pascal's Triangle</a></li><li><a href="../scrabble-score/README.zh.html"><strong aria-hidden="true">3.12.</strong> 字母的分数游戏 &gt;&lt; Scrabble Score</a></li><li><a href="../pangram/README.zh.html"><strong aria-hidden="true">3.13.</strong> 全字母句 &gt;&lt; Pangram</a></li><li><a href="../paasio/README.zh.html"><strong aria-hidden="true">3.14.</strong> PaaS-IO-报告 &gt;&lt; Paasio</a></li><li><a href="../nucleotide-count/README.zh.html"><strong aria-hidden="true">3.15.</strong> 核苷酸计数 &gt;&lt; Nucleotide Count</a></li><li><a href="../luhn/README.zh.html"><strong aria-hidden="true">3.16.</strong> 模 10 算法 &gt;&lt; Luhn</a></li><li><a href="../largest-series-product/README.zh.html"><strong aria-hidden="true">3.17.</strong> 最大数字子串乘积 &gt;&lt; Largest Series Product</a></li><li><a href="../word-count/README.zh.html"><strong aria-hidden="true">3.18.</strong> 单词计数 &gt;&lt; Word Count</a></li><li><a href="../atbash-cipher/README.zh.html"><strong aria-hidden="true">3.19.</strong> Atbash 加密 &gt;&lt; Atbash Cipher</a></li><li><a href="../crypto-square/README.zh.html"><strong aria-hidden="true">3.20.</strong> 密码矩形 &gt;&lt; Crypto Square</a></li><li><a href="../rotational-cipher/README.zh.html"><strong aria-hidden="true">3.21.</strong> 旋转密码 &gt;&lt; Rotational Cipher</a></li><li><a href="../simple-cipher/README.zh.html"><strong aria-hidden="true">3.22.</strong> 简单加密 &gt;&lt; Simple Cipher</a></li><li><a href="../rail-fence-cipher/README.zh.html"><strong aria-hidden="true">3.23.</strong> 栅栏密码 &gt;&lt; Rail Fence Cipher</a></li><li><a href="../etl/README.zh.html"><strong aria-hidden="true">3.24.</strong> ETL</a></li><li><a href="../accumulate/README.zh.html"><strong aria-hidden="true">3.25.</strong> 集合操作 &gt;&lt; Accumulate</a></li><li><a href="../acronym/README.zh.html"><strong aria-hidden="true">3.26.</strong> 术语 &gt;&lt; Acronym</a></li><li><a href="../sieve/README.zh.html"><strong aria-hidden="true">3.27.</strong> 素数筛 &gt;&lt; Sieve</a></li><li><a href="../rna-transcription/README.zh.html"><strong aria-hidden="true">3.28.</strong> RNA 转录 &gt;&lt; RNA Transcription</a></li><li><a href="../triangle/README.zh.html"><strong aria-hidden="true">3.29.</strong> 三角形</a></li><li><a href="../roman-numerals/README.zh.html"><strong aria-hidden="true">3.30.</strong> 罗马数字 &gt;&lt; Roman Numerals</a></li><li><a href="../all-your-base/README.zh.html"><strong aria-hidden="true">3.31.</strong> 你所的基本</a></li><li><a href="../grade-school/README.zh.html"><strong aria-hidden="true">3.32.</strong> 学册</a></li><li><a href="../binary-search/README.zh.html"><strong aria-hidden="true">3.33.</strong> 二分查找</a></li><li><a href="../robot-simulator/README.zh.html"><strong aria-hidden="true">3.34.</strong> 机器人模拟器</a></li><li><a href="../bracket-push/README.zh.html"><strong aria-hidden="true">3.35.</strong> 括号配套</a></li><li><a href="../luhn-from/README.zh.html"><strong aria-hidden="true">3.36.</strong> Luhn From</a></li><li><a href="../queen-attack/README.zh.html"><strong aria-hidden="true">3.37.</strong> 皇后 攻击</a></li><li><a href="../bowling/README.zh.html"><strong aria-hidden="true">3.38.</strong> 保龄球</a></li><li><a href="../sublist/README.zh.html"><strong aria-hidden="true">3.39.</strong> 子列表</a></li><li><a href="../space-age/README.zh.html"><strong aria-hidden="true">3.40.</strong> 地球年</a></li><li><a href="../luhn-trait/README.zh.html"><strong aria-hidden="true">3.41.</strong> Luhn Trait</a></li><li><a href="../macros/README.zh.html"><strong aria-hidden="true">3.42.</strong> 宏</a></li><li><a href="../allergies/README.zh.html"><strong aria-hidden="true">3.43.</strong> 过敏</a></li><li><a href="../variable-length-quantity/README.zh.html"><strong aria-hidden="true">3.44.</strong> 可变长度数量</a></li><li><a href="../phone-number/README.zh.html"><strong aria-hidden="true">3.45.</strong> 电话号码</a></li><li><a href="../wordy/README.zh.html"><strong aria-hidden="true">3.46.</strong> 罗唆</a></li><li><a href="../tournament/README.zh.html"><strong aria-hidden="true">3.47.</strong> 比赛</a></li><li><a href="../custom-set/README.zh.html"><strong aria-hidden="true">3.48.</strong> 自定义 set</a></li><li><a href="../alphametics/README.zh.html"><strong aria-hidden="true">3.49.</strong> 字母谜题</a></li><li><a href="../two-bucket/README.zh.html"><strong aria-hidden="true">3.50.</strong> 两个桶</a></li><li><a href="../pig-latin/README.zh.html"><strong aria-hidden="true">3.51.</strong> 猪的拉丁文</a></li><li><a href="../diamond/README.zh.html"><strong aria-hidden="true">3.52.</strong> 钻石</a></li><li><a href="../spiral-matrix/README.zh.html"><strong aria-hidden="true">3.53.</strong> 螺旋矩阵</a></li><li><a href="../palindrome-products/README.zh.html"><strong aria-hidden="true">3.54.</strong> 回文产品</a></li><li><a href="../poker/README.zh.html"><strong aria-hidden="true">3.55.</strong> 扑克</a></li><li><a href="../grep/README.zh.html"><strong aria-hidden="true">3.56.</strong> grep</a></li><li><a href="../scale-generator/README.zh.html" class="active"><strong aria-hidden="true">3.57.</strong> 音阶生成器</a></li><li><a href="../decimal/README.zh.html"><strong aria-hidden="true">3.58.</strong> 十进制</a></li><li><a href="../anagram/README.zh.html"><strong aria-hidden="true">3.59.</strong> 字谜</a></li><li><a href="../protein-translation/README.zh.html"><strong aria-hidden="true">3.60.</strong> 蛋白质翻译</a></li><li><a href="../robot-name/README.zh.html"><strong aria-hidden="true">3.61.</strong> 机器人名称</a></li><li><a href="../book-store/README.zh.html"><strong aria-hidden="true">3.62.</strong> 书店</a></li></ol></li><li><a href="../high.html"><strong aria-hidden="true">4.</strong> 难</a></li><li><ol class="section"><li><a href="../ocr-numbers/README.zh.html"><strong aria-hidden="true">4.1.</strong> OCR 号码</a></li><li><a href="../minesweeper/README.zh.html"><strong aria-hidden="true">4.2.</strong> 扫雷</a></li><li><a href="../dominoes/README.zh.html"><strong aria-hidden="true">4.3.</strong> 骨牌</a></li><li><a href="../parallel-letter-frequency/README.zh.html"><strong aria-hidden="true">4.4.</strong> 并行字母频率</a></li><li><a href="../rectangles/README.zh.html"><strong aria-hidden="true">4.5.</strong> 矩形</a></li><li><a href="../forth/README.zh.html"><strong aria-hidden="true">4.6.</strong> Forth</a></li><li><a href="../circular-buffer/README.zh.html"><strong aria-hidden="true">4.7.</strong> 循环缓冲区</a></li><li><a href="../react/README.zh.html"><strong aria-hidden="true">4.8.</strong> React</a></li></ol></li><li><a href="../untag.html"><strong aria-hidden="true">5.</strong> 未标签</a></li><li><ol class="section"><li><a href="../hexadecimal/README.zh.html"><strong aria-hidden="true">5.1.</strong> 十六进制</a></li><li><a href="../nucleotide-codons/README.zh.html"><strong aria-hidden="true">5.2.</strong> 核苷酸密码子</a></li><li><a href="../two-fer/README.zh.html"><strong aria-hidden="true">5.3.</strong> two-fer</a></li><li class="spacer"></li></ol></li><li><a href="../add-test-code.html">修改 mdBook 主题</a></li></ol>
    </nav>

    <div id="page-wrapper" class="page-wrapper">

        <div class="page">
            
            <div id="menu-bar" class="menu-bar">
                <div id="menu-bar-sticky-container">
                    <div class="left-buttons">
                        <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents"
                            aria-label="Toggle Table of Contents" aria-controls="sidebar">
                            <i class="fa fa-bars"></i>
                        </button>
                        <!-- START - Rust Cookbook customization -->
                        <button id="edit-button" class="icon-button" type="button" title="Fork and edit" aria-label="Fork and edit"
                            aria-haspopup="true" aria-expanded="false" aria-controls="edit">
                            <i class="fa fa-edit">Edit</i>
                        </button>
                        <!-- END - Rust Cookbook customization -->
                        <button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme"
                            aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
                            <i class="fa fa-paint-brush"></i>
                        </button>
                        <ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
                            <li role="none"><button role="menuitem" class="theme" id="light">Light <span class="default">(default)</span></button></li>
                            <li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
                            <li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
                        </ul>
                        
                        <button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)"
                            aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
                            <i class="fa fa-search"></i>
                        </button>
                        
                    </div>

                    <h1 class="menu-title">exercisms.io 快速练习</h1>
                    
                        <div class="right-buttons">
                            <a href="../print.html" title="Print this book" aria-label="Print this book">
                                <i id="print-button" class="fa fa-print"></i>
                            </a>
                            
                        </div>
                    </div>
                </div>

            
            <div id="search-wrapper" class="hidden">
                <form id="searchbar-outer" class="searchbar-outer">
                    <input type="search" name="search" id="searchbar" name="searchbar" placeholder="Search this book ..."
                        aria-controls="searchresults-outer" aria-describedby="searchresults-header">
                </form>
                <div id="searchresults-outer" class="searchresults-outer hidden">
                    <div id="searchresults-header" class="searchresults-header"></div>
                    <ul id="searchresults">
                    </ul>
                </div>
            </div>
            

            <!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
            <script type="text/javascript">
                document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
                document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
                Array.from(document.querySelectorAll('#sidebar a')).forEach(function (link) {
                    link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
                });
            </script>

            <!-- // START - Rust Cookbook customization -->
            <script>
                document.getElementById("edit-button").addEventListener("click", function () {
                    var editWindow = window.open("https://github.com/chinanf-boy/exercism-rust-zh/edit/master/src/scale-generator/README.zh.md");
                });</script>
            <!-- // END - Rust Cookbook customization -->

            <div id="content" class="content">
                <main>
                    <a class="header" href="#scale-generator" id="scale-generator"><h1>Scale Generator</h1></a>
<a class="header" href="#1-readme" id="1-readme"><h2>1. Readme</h2></a>
<a class="header" href="#音阶生成器" id="音阶生成器"><h1>音阶生成器</h1></a>
<blockquote>
<p>译：真不知道说什么。</p>
</blockquote>
<p>给出一个音调，或是开始的音符以及一组间隔，从音调开始并遵循指定的间隔模式生成音阶。</p>
<p>西方音乐中的音阶，基于彩色(12 音符)音阶。该音阶可表示为以下一组音高:</p>
<p>A，A#，B，C，C#，D，D#，E，F，F#，G，G#</p>
<p>给出一个尖音符(用#表示)也可以表示为它前面的平音符(用 b 表示)，所以半音音阶也可以这样写:</p>
<p>A，Bb，B，C，Db，D，Eb，E，F，Gb，G，Ab</p>
<p>大调和小调的音阶和模式是这个十二音高集合的子集。它们有七个音高，称为全音阶音阶。这些音阶中的音符集合使用锐利或平面，根据音调。这是一个列表，其中包括:</p>
<p>没有尖(Sharps)或平(Flats)：C 大调（major）和 a 小调(minor)</p>
<p>使用 Sharps:
G, D, A, E, B, F# major
e, b, f#, c#, g#, d# minor</p>
<p>使用 Flats:
F, Bb, Eb, Ab, Db, Gb major
d, g, c, f, bb, eb minor</p>
<p>全音阶音阶以及源自半音音阶的所有其他音阶都是间隔建立的。间隔是两个音高之间的间距。</p>
<p>最简单的间隔是在两个相邻音符之间，称为”半步”或”小秒”(有时写为小写”m”)。具有中间音符的两个音符之间的间隔称为”整步”或”大秒”(写为大写”M”)。仅使用相邻音符之间的这两个间隔，来构建全音阶音阶。</p>
<p>非全音阶音阶可以包含其他间隔。一个”先增强”的间隔，写上 A，具有两个中间音符(例如，从 A 到 C 或从 D 到 E)。间隔也越来越小，但他们不会参与这个练习。</p>
<a class="header" href="#2-开始你的表演" id="2-开始你的表演"><h2>2. 开始你的表演</h2></a>
<pre><pre class="playpen"><code class="language-rust editable">// You should change this.
//
// Depending on your implementation, there are a variety of potential errors
// which might occur. They aren't checked by the test suite in order to
// allow the greatest freedom of implementation, but real libraries should
// provide useful, descriptive errors so that downstream code can react
// appropriately.
//
// One common idiom is to define an Error enum which wraps all potential
// errors. Another common idiom is to use a helper type such as failure::Error
// which does more or less the same thing but automatically.
pub type Error = ();

pub struct Scale;

impl Scale {
   pub fn new(tonic: &amp;str, intervals: &amp;str) -&gt; Result&lt;Scale, Error&gt; {
       unimplemented!(
           &quot;Construct a new scale with tonic {} and intervals {}&quot;,
           tonic,
           intervals
       )
   }

   pub fn chromatic(tonic: &amp;str) -&gt; Result&lt;Scale, Error&gt; {
       unimplemented!(&quot;Construct a new chromatic scale with tonic {}&quot;, tonic)
   }

   pub fn enumerate(&amp;self) -&gt; Vec&lt;String&gt; {
       unimplemented!()
   }
}

</code></pre></pre>
<a class="header" href="#3-测试代码查看" id="3-测试代码查看"><h2>3. 测试代码查看</h2></a>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
/// Tests for scale-generator
///
/// Generated by [script][script] using [canonical data][canonical-data]
///
/// [script]: https://github.com/exercism/rust/blob/master/bin/init_exercise.py
/// [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/master/exercises/scale-generator/canonical_data.json

/// Process a single test case for the property `chromatic`
///
/// All cases for the `chromatic` property are implemented
/// in terms of this function.
fn process_chromatic_case(tonic: &amp;str, expected: &amp;[&amp;str]) {
   let s = Scale::chromatic(tonic).unwrap();
   assert_eq!(s.enumerate(), expected);
}

/// Process a single test case for the property `interval`
///
/// All cases for the `interval` property are implemented
/// in terms of this function.
fn process_interval_case(tonic: &amp;str, intervals: &amp;str, expected: &amp;[&amp;str]) {
   let s = Scale::new(tonic, intervals).unwrap();
   assert_eq!(s.enumerate(), expected);
}

// Chromatic scales
// These tests have no interval.
// The chromatic scale is considered the default scale

#[test]
/// Chromatic scale with sharps
fn test_chromatic_scale_with_sharps() {
   process_chromatic_case(
       &quot;C&quot;,
       &amp;[
           &quot;C&quot;, &quot;C#&quot;, &quot;D&quot;, &quot;D#&quot;, &quot;E&quot;, &quot;F&quot;, &quot;F#&quot;, &quot;G&quot;, &quot;G#&quot;, &quot;A&quot;, &quot;A#&quot;, &quot;B&quot;,
       ],
   );
}

#[test]
//#[ignore]
/// Chromatic scale with flats
fn test_chromatic_scale_with_flats() {
   process_chromatic_case(
       &quot;F&quot;,
       &amp;[
           &quot;F&quot;, &quot;Gb&quot;, &quot;G&quot;, &quot;Ab&quot;, &quot;A&quot;, &quot;Bb&quot;, &quot;B&quot;, &quot;C&quot;, &quot;Db&quot;, &quot;D&quot;, &quot;Eb&quot;, &quot;E&quot;,
       ],
   );
}

// Scales with specified intervals
// These tests all have intervals and are explorations of different
// traversals of the scale.

#[test]
//#[ignore]
/// Simple major scale
///
/// The simplest major scale, with no sharps or flats.
fn test_simple_major_scale() {
   process_interval_case(&quot;C&quot;, &quot;MMmMMMm&quot;, &amp;[&quot;C&quot;, &quot;D&quot;, &quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;B&quot;]);
}

#[test]
//#[ignore]
/// Major scale with sharps
fn test_major_scale_with_sharps() {
   process_interval_case(&quot;G&quot;, &quot;MMmMMMm&quot;, &amp;[&quot;G&quot;, &quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;, &quot;F#&quot;]);
}

#[test]
//#[ignore]
/// Major scale with flats
fn test_major_scale_with_flats() {
   process_interval_case(&quot;F&quot;, &quot;MMmMMMm&quot;, &amp;[&quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;Bb&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;]);
}

#[test]
//#[ignore]
/// Minor scale with sharps
fn test_minor_scale_with_sharps() {
   process_interval_case(&quot;f#&quot;, &quot;MmMMmMM&quot;, &amp;[&quot;F#&quot;, &quot;G#&quot;, &quot;A&quot;, &quot;B&quot;, &quot;C#&quot;, &quot;D&quot;, &quot;E&quot;]);
}

#[test]
//#[ignore]
/// Minor scale with flats
fn test_minor_scale_with_flats() {
   process_interval_case(&quot;bb&quot;, &quot;MmMMmMM&quot;, &amp;[&quot;Bb&quot;, &quot;C&quot;, &quot;Db&quot;, &quot;Eb&quot;, &quot;F&quot;, &quot;Gb&quot;, &quot;Ab&quot;]);
}

#[test]
//#[ignore]
/// Dorian mode
fn test_dorian_mode() {
   process_interval_case(&quot;d&quot;, &quot;MmMMMmM&quot;, &amp;[&quot;D&quot;, &quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;B&quot;, &quot;C&quot;]);
}

#[test]
//#[ignore]
/// Mixolydian mode
fn test_mixolydian_mode() {
   process_interval_case(&quot;Eb&quot;, &quot;MMmMMmM&quot;, &amp;[&quot;Eb&quot;, &quot;F&quot;, &quot;G&quot;, &quot;Ab&quot;, &quot;Bb&quot;, &quot;C&quot;, &quot;Db&quot;]);
}

#[test]
//#[ignore]
/// Lydian mode
fn test_lydian_mode() {
   process_interval_case(&quot;a&quot;, &quot;MMMmMMm&quot;, &amp;[&quot;A&quot;, &quot;B&quot;, &quot;C#&quot;, &quot;D#&quot;, &quot;E&quot;, &quot;F#&quot;, &quot;G#&quot;]);
}

#[test]
//#[ignore]
/// Phrygian mode
fn test_phrygian_mode() {
   process_interval_case(&quot;e&quot;, &quot;mMMMmMM&quot;, &amp;[&quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;]);
}

#[test]
//#[ignore]
/// Locrian mode
fn test_locrian_mode() {
   process_interval_case(&quot;g&quot;, &quot;mMMmMMM&quot;, &amp;[&quot;G&quot;, &quot;Ab&quot;, &quot;Bb&quot;, &quot;C&quot;, &quot;Db&quot;, &quot;Eb&quot;, &quot;F&quot;]);
}

#[test]
//#[ignore]
/// Harmonic minor
///
/// Note that this case introduces the accidental interval (A)
fn test_harmonic_minor() {
   process_interval_case(&quot;d&quot;, &quot;MmMMmAm&quot;, &amp;[&quot;D&quot;, &quot;E&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;Bb&quot;, &quot;Db&quot;]);
}

#[test]
//#[ignore]
/// Octatonic
fn test_octatonic() {
   process_interval_case(
       &quot;C&quot;,
       &quot;MmMmMmMm&quot;,
       &amp;[&quot;C&quot;, &quot;D&quot;, &quot;D#&quot;, &quot;F&quot;, &quot;F#&quot;, &quot;G#&quot;, &quot;A&quot;, &quot;B&quot;],
   );
}

#[test]
//#[ignore]
/// Hexatonic
fn test_hexatonic() {
   process_interval_case(&quot;Db&quot;, &quot;MMMMMM&quot;, &amp;[&quot;Db&quot;, &quot;Eb&quot;, &quot;F&quot;, &quot;G&quot;, &quot;A&quot;, &quot;B&quot;]);
}

#[test]
//#[ignore]
/// Pentatonic
fn test_pentatonic() {
   process_interval_case(&quot;A&quot;, &quot;MMAMA&quot;, &amp;[&quot;A&quot;, &quot;B&quot;, &quot;C#&quot;, &quot;E&quot;, &quot;F#&quot;]);
}

#[test]
//#[ignore]
/// Enigmatic
fn test_enigmatic() {
   process_interval_case(&quot;G&quot;, &quot;mAMMMmm&quot;, &amp;[&quot;G&quot;, &quot;G#&quot;, &quot;B&quot;, &quot;C#&quot;, &quot;D#&quot;, &quot;F&quot;, &quot;F#&quot;]);
}

#}</code></pre></pre>
<a class="header" href="#4-答案" id="4-答案"><h2>4. 答案</h2></a>
<p><details></p>
<pre><pre class="playpen"><code class="language-rust">
# #![allow(unused_variables)]
#fn main() {
#[macro_use]
extern crate enum_primitive_derive;
extern crate failure;
#[macro_use]
extern crate failure_derive;
extern crate itertools;
extern crate num_traits;

pub use self::interval::{Interval, Intervals};
use self::note::Accidental;
pub use self::note::Note;
use failure::Error;
use std::str::FromStr;

pub mod interval {
   use itertools::Itertools;
   use std::fmt;
   use std::ops::Deref;
   use std::str::FromStr;

   #[derive(Debug, Clone, Copy, PartialEq, Eq, Fail)]
   pub enum ParseErr {
       #[fail(display = &quot;invalid interval&quot;)]
       InvalidInterval,
       #[fail(display = &quot;wrong number of semitones&quot;)]
       WrongNumberOfSemitones,
   }

   #[derive(Debug, Clone, Copy, PartialEq, Eq, Primitive)]
   pub enum Interval {
       HalfStep = 1,
       WholeStep = 2,
       AugmentedFirst = 3,
   }

   impl fmt::Display for Interval {
       fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
           use self::Interval::*;
           write!(
               f,
               &quot;{}&quot;,
               match self {
                   HalfStep =&gt; &quot;m&quot;,
                   WholeStep =&gt; &quot;M&quot;,
                   AugmentedFirst =&gt; &quot;A&quot;,
               }
           )
       }
   }

   impl FromStr for Interval {
       type Err = ParseErr;

       fn from_str(s: &amp;str) -&gt; Result&lt;Self, Self::Err&gt; {
           use self::Interval::*;
           match s {
               &quot;m&quot; =&gt; Ok(HalfStep),
               &quot;M&quot; =&gt; Ok(WholeStep),
               &quot;A&quot; =&gt; Ok(AugmentedFirst),
               _ =&gt; Err(ParseErr::InvalidInterval),
           }
       }
   }

   #[derive(Debug, Clone, PartialEq, Eq)]
   pub struct Intervals(Vec&lt;Interval&gt;);

   impl fmt::Display for Intervals {
       fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
           write!(f, &quot;{}&quot;, self.0.iter().join(&quot;&quot;))
       }
   }

   impl FromStr for Intervals {
       type Err = ParseErr;

       fn from_str(s: &amp;str) -&gt; Result&lt;Self, Self::Err&gt; {
           let mut semitones = Vec::with_capacity(s.len());

           for (i, c) in s.char_indices() {
               semitones.push(Interval::from_str(&amp;s[i..i + c.len_utf8()])?);
           }

           if semitones.iter().take(12).map(|&amp;i| i as u8).sum::&lt;u8&gt;() == 12 {
               Ok(Intervals(semitones))
           } else {
               Err(ParseErr::WrongNumberOfSemitones)
           }
       }
   }

   impl Deref for Intervals {
       type Target = Vec&lt;Interval&gt;;

       fn deref(&amp;self) -&gt; &amp;Self::Target {
           &amp;self.0
       }
   }

   #[cfg(test)]
   mod test {
       use super::*;

       #[test]
       fn test_parse_chromatic() {
           assert!(&quot;mmmmmmmmmmmm&quot;.parse::&lt;Intervals&gt;().is_ok());
       }

       #[test]
       fn test_parse_major() {
           assert!(&quot;MMmMMMm&quot;.parse::&lt;Intervals&gt;().is_ok());
       }

       #[test]
       fn test_parse_minor() {
           assert!(&quot;MmMMmMM&quot;.parse::&lt;Intervals&gt;().is_ok());
       }
   }
}

pub mod note {
   use interval::Interval;
   use num_traits::{FromPrimitive, ToPrimitive};
   use std::fmt;
   use std::ops::AddAssign;
   use std::str::FromStr;

   pub const SEMITONES: i8 = 12;

   #[derive(Debug, Clone, Copy, PartialEq, Eq, Primitive)]
   pub enum Semitone {
       A = 0,
       ASharp = 1,
       B = 2,
       C = 3,
       CSharp = 4,
       D = 5,
       DSharp = 6,
       E = 7,
       F = 8,
       FSharp = 9,
       G = 10,
       GSharp = 11,
   }

   #[derive(Debug, Clone, Copy, PartialEq, Eq, Primitive)]
   pub enum Root {
       A = 0,
       B = 2,
       C = 3,
       D = 5,
       E = 7,
       F = 8,
       G = 10,
   }

   impl fmt::Display for Root {
       fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
           write!(f, &quot;{:?}&quot;, self)
       }
   }

   #[derive(Debug, Clone, Copy, PartialEq, Eq)]
   pub enum Accidental {
       Sharp,
       Flat,
   }

   impl Accidental {
       fn to_i8(&amp;self) -&gt; i8 {
           match *self {
               Accidental::Sharp =&gt; 1,
               Accidental::Flat =&gt; -1,
           }
       }

       pub fn from_tonic(tonic: &amp;str) -&gt; Accidental {
           match tonic {
               &quot;C&quot; | &quot;a&quot; | &quot;G&quot; | &quot;D&quot; | &quot;A&quot; | &quot;E&quot; | &quot;B&quot; | &quot;F#&quot; | &quot;e&quot; | &quot;b&quot; | &quot;f#&quot; | &quot;c#&quot; | &quot;g#&quot;
               | &quot;d#&quot; =&gt; Accidental::Sharp,
               _ =&gt; Accidental::Flat,
           }
       }
   }

   impl fmt::Display for Accidental {
       fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
           write!(
               f,
               &quot;{}&quot;,
               match &amp;self {
                   Accidental::Sharp =&gt; '#',
                   Accidental::Flat =&gt; 'b',
               }
           )
       }
   }

   #[derive(Debug, Clone, Copy, PartialEq, Eq)]
   pub struct Note {
       tonic: Root,
       accidental: Option&lt;Accidental&gt;,
   }

   impl Note {
       pub fn canonicalize(&amp;self, lean: Accidental) -&gt; Note {
           let mut n: Note = Semitone::from(*self).into();
           if let Some(accidental) = n.accidental {
               if accidental != lean {
                   if lean == Accidental::Flat {
                       n += Interval::HalfStep;
                       n.accidental = Some(Accidental::Flat);
                   }
               }
           }
           n
       }
   }

   impl fmt::Display for Note {
       fn fmt(&amp;self, f: &amp;mut fmt::Formatter) -&gt; fmt::Result {
           write!(
               f,
               &quot;{}{}&quot;,
               self.tonic,
               self.accidental.map_or(String::new(), |a| a.to_string()),
           )
       }
   }

   #[derive(Debug, Clone, Copy, PartialEq, Eq, Fail)]
   pub enum ParseErr {
       #[fail(display = &quot;invalid length&quot;)]
       InvalidLength,
       #[fail(display = &quot;invalid tonic&quot;)]
       InvalidTonic,
       #[fail(display = &quot;invalid accidental&quot;)]
       InvalidAccidental,
   }

   impl FromStr for Note {
       type Err = ParseErr;

       fn from_str(s: &amp;str) -&gt; Result&lt;Self, Self::Err&gt; {
           let lc = s.to_lowercase();
           let mut iter = lc.chars();

           let mut note = match iter.next() {
               Some(c) if 'a' &lt;= c &amp;&amp; 'g' &gt;= c =&gt; Note {
                   tonic: match c {
                       'a' =&gt; Root::A,
                       'b' =&gt; Root::B,
                       'c' =&gt; Root::C,
                       'd' =&gt; Root::D,
                       'e' =&gt; Root::E,
                       'f' =&gt; Root::F,
                       'g' =&gt; Root::G,
                       _ =&gt; return Err(ParseErr::InvalidTonic),
                   },
                   accidental: None,
               },
               Some(_) =&gt; return Err(ParseErr::InvalidTonic),
               None =&gt; return Err(ParseErr::InvalidLength),
           };

           match iter.next() {
               Some('b') =&gt; note.accidental = Some(Accidental::Flat),
               Some('#') =&gt; note.accidental = Some(Accidental::Sharp),
               Some(_) =&gt; return Err(ParseErr::InvalidAccidental),
               None =&gt; {}
           }

           if iter.next().is_some() {
               return Err(ParseErr::InvalidLength);
           }

           Ok(note)
       }
   }

   impl From&lt;Semitone&gt; for Note {
       fn from(s: Semitone) -&gt; Self {
           Note {
               tonic: match s {
                   Semitone::A | Semitone::ASharp =&gt; Root::A,
                   Semitone::B =&gt; Root::B,
                   Semitone::C | Semitone::CSharp =&gt; Root::C,
                   Semitone::D | Semitone::DSharp =&gt; Root::D,
                   Semitone::E =&gt; Root::E,
                   Semitone::F | Semitone::FSharp =&gt; Root::F,
                   Semitone::G | Semitone::GSharp =&gt; Root::G,
               },
               accidental: match s {
                   Semitone::ASharp
                   | Semitone::CSharp
                   | Semitone::DSharp
                   | Semitone::FSharp
                   | Semitone::GSharp =&gt; Some(Accidental::Sharp),
                   _ =&gt; None,
               },
           }
       }
   }

   impl From&lt;Note&gt; for Semitone {
       fn from(n: Note) -&gt; Self {
           Semitone::from_i8(
               (SEMITONES + n.tonic.to_i8().unwrap() + n.accidental.map_or(0, |a| a.to_i8()))
                   % SEMITONES,
           ).expect(&quot;must have valid semitone&quot;)
       }
   }

   impl AddAssign&lt;Interval&gt; for Note {
       fn add_assign(&amp;mut self, rhs: Interval) {
           *self = Semitone::from_i8(
               (SEMITONES + Semitone::from(*self).to_i8().unwrap() + rhs.to_i8().unwrap())
                   % SEMITONES,
           ).unwrap()
               .into();
       }
   }

}

#[derive(Debug)]
pub struct Scale {
   tonic: Note,
   lean: Accidental,
   intervals: Intervals,
}

impl Scale {
   pub fn new(tonic: &amp;str, intervals: &amp;str) -&gt; Result&lt;Scale, Error&gt; {
       Ok(Scale {
           tonic: Note::from_str(tonic)?,
           lean: Accidental::from_tonic(tonic),
           intervals: Intervals::from_str(intervals)?,
       })
   }

   pub fn chromatic(tonic: &amp;str) -&gt; Result&lt;Scale, Error&gt; {
       Scale::new(tonic, &quot;mmmmmmmmmmmm&quot;)
   }

   pub fn enumerate(&amp;self) -&gt; Vec&lt;String&gt; {
       let mut out = Vec::with_capacity(self.intervals.len());

       let mut note = self.tonic;
       for &amp;interval in self.intervals.iter() {
           out.push(note.canonicalize(self.lean).to_string());
           note += interval;
       }

       out
   }
}

#}</code></pre></pre>
<p></details></p>
<hr />
<hr />
<a class="header" href="#填充相关" id="填充相关"><h2>填充/相关</h2></a>

                </main>

                <nav class="nav-wrapper" aria-label="Page navigation">
                    <!-- Mobile navigation buttons -->
                    
                    <a rel="prev" href="../grep/README.zh.html" class="mobile-nav-chapters previous" title="Previous chapter"
                        aria-label="Previous chapter" aria-keyshortcuts="Left">
                        <i class="fa fa-angle-left"></i>
                    </a>
                    

                    
                    <a rel="next" href="../decimal/README.zh.html" class="mobile-nav-chapters next" title="Next chapter"
                        aria-label="Next chapter" aria-keyshortcuts="Right">
                        <i class="fa fa-angle-right"></i>
                    </a>
                    

                    <div style="clear: both"></div>
                </nav>
            </div>
        </div>

        <nav class="nav-wide-wrapper" aria-label="Page navigation">
            
            <a href="../grep/README.zh.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter"
                aria-keyshortcuts="Left">
                <i class="fa fa-angle-left"></i>
            </a>
            

            
            <a href="../decimal/README.zh.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter"
                aria-keyshortcuts="Right">
                <i class="fa fa-angle-right"></i>
            </a>
            
        </nav>

    </div>

    

    
    <!-- Google Analytics Tag -->
    <script async src="https://www.googletagmanager.com/gtag/js?id=UA-128555056-1"></script>
    
    <script type="text/javascript">
        var localAddrs = ["localhost", "127.0.0.1", ""];
        if (localAddrs.indexOf(document.location.hostname) === -1) {
            window.dataLayer = window.dataLayer || [];
            function gtag() { dataLayer.push(arguments); }
            gtag('js', new Date());

            gtag('config', 'UA-128555056-1');
        }
    </script>
    

    
    <script src="../ace.js" type="text/javascript" charset="utf-8"></script>
    <script src="../editor.js" type="text/javascript" charset="utf-8"></script>
    <script src="../mode-rust.js" type="text/javascript" charset="utf-8"></script>
    <script src="../theme-dawn.js" type="text/javascript" charset="utf-8"></script>
    <script src="../theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
    

    
    <script src="../elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../mark.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../searcher.js" type="text/javascript" charset="utf-8"></script>
    

    <script src="../clipboard.min.js" type="text/javascript" charset="utf-8"></script>
    <script src="../highlight.js" type="text/javascript" charset="utf-8"></script>
    <script src="../book.js" type="text/javascript" charset="utf-8"></script>

    <!-- Custom JS scripts -->
    

    

</body>

</html>